AWS Amplify + Reactで既存のLambdaを呼び出す
既存のLambdaをAWS Amplify + Reactから呼び出してみました。
背景
Step Functionsなどを用いて、Lambdaを単機能で疎結合な構成で実装している場合、他のインタフェースからも呼び出して再利用したい場合があるかと思います。AWS AmplifyにはLambdaを追加定義するamplify add function
コマンドとamplify add api
コマンドで追加したAPIからLambdaを呼び出す@function
ディレクティブがありますが、今回はシンプルにReactからaws-sdk
のLambda Clientを使ってLambdaを呼び出してみました。
解法
import React, { useState } from 'react' import { Auth } from 'aws-amplify' import { LambdaClient, InvokeCommand } from '@aws-sdk/client-lambda' import Box from '@mui/material/Box' import TextField from '@mui/material/TextField' import Button from '@mui/material/Button' const initialState = { lambdaArgs1: '', lambdaArgs2: '' } const SomeLambdaInvoker = () => { const [formState, setFormState] = useState(initialState) const setInput = (key, value) => { setFormState({ ...formState, [key]: value }) } const invoke = async() => { const credentials = await Auth.currentCredentials() const client = new LambdaClient({ credentials: Auth.essentialCredentials(credentials), region: process.env.REACT_APP_LAMBDA_REGION }) const input = { FunctionName: 'some-existing-lambda-' + process.env.REACT_APP_STAGE, Payload: JSON.stringify(formState) } const command = new InvokeCommand(input) const response = await client.send(command) if (response) { setFormState(initialState) } else { console.error(response.error) } } return ( <Box sx={{ p: 2, bgcolor: 'background.default', display: 'grid', gridTemplateColumns: { md: '1fr 1fr' }, gap: 2, }} > <TextField required id="lambda-args1" label="Lambda関数への引数1" type="text" onChange={event => setInput('lambdaArgs1', event.target.value)} value={formState.lambdaArgs1} /> <TextField required id="lambda-args2" label="Lambda関数への引数2" type="text" onChange={event => setInput('lambdaArgs2', event.target.value)} value={formState.lambdaArgs2} /> <Button variant="contained" onClick={invoke} > Submit </Button> </Box> ) } export default SomeLambdaInvoker
some-existing-lambda-{dev|prod}
という名前の既存のLambdaを呼び出す例になっています。
LambdaClient
コンストラクタにはAmplifyのAuthモジュールから得たクレデンシャルを指定します。Amplifyの認証を通ったユーザのみにLambdaの呼び出しを許可しています。なお、Amplifyの実行ロールにはlambda:InvokeFunction
権限が必要です。ポリシー設定は下記のように行います。Resourceで実行を許可するLambda関数のARNを厳密に絞り込むことをオススメします。
{ "Version": "2012-10-17", "Statement": [ { "Sid": "LambdaInvokeSetting", "Effect": "Allow", "Action": "lambda:InvokeFunction", "Resource": "arn:aws:lambda:ap-northeast-1:xxxxxxxxxxxx:function:some-existing-lambda-dev" } ] }
InvokeCommand
コンストラクタにFunctionName
で呼び出したいLambda名、Payload
にLambdaで与えたいパラメータを指定し、LambdaClient.send(command)
メソッドでLambda呼び出しを実行します。
なお、.env
ファイルには下記のような設定をしています。環境に合わせて書き換えてください。
REACT_APP_STAGE=dev REACT_APP_LAMBDA_REGION=ap-northeast-1
Lambdaの方は次のように書かれている想定です。
def lambda_handler(event, context): print(event['lambdaArgs1']) # lambdaArgs1にアクセス print(event['lambdaArgs2']) # lambdaArgs2にアクセス """ : いろいろな処理をする : """ if __name__ == "__main__": lambda_handler({"lambdaArgs1": "", "lambdaArgs2": ""}, {})
まとめ
aws-sdk
のLambda Clientを使って既存のLambdaを呼び出してみました。Amplifyの実行ロールの権限を絞って、かつ、AmplifyのAuthモジュールと組み合わせることでCognitoで認証したユーザーに限定して既存の特定のLambdaだけを実行させることができます。参考になれば幸いです。